#include <fs.h>
extern size_t serial_write(const void *buf, size_t offset, size_t len); 
extern size_t events_read(void *buf, size_t offset, size_t len) ;
extern size_t dispinfo_read(void *buf, size_t offset, size_t len) ;
extern size_t fb_write(const void *buf, size_t offset, size_t len);
typedef size_t (*ReadFn) (void *buf, size_t offset, size_t len);
typedef size_t (*WriteFn) (const void *buf, size_t offset, size_t len);
extern size_t ramdisk_read(void *buf, size_t offset, size_t len);
size_t ramdisk_write(const void *buf, size_t offset, size_t len) ;
int fs_open(const char *pathname, int flags, int mode);
size_t fs_read(int fd, void *buf, size_t len);
size_t fs_write(int fd, const void *buf, size_t len);
size_t fs_lseek(int fd, size_t offset, int whence);
int fs_close(int fd);
typedef struct {
  char *name;
  size_t size;
  size_t disk_offset;
  ReadFn read;
  WriteFn write;
  size_t open_offset;
} Finfo;
enum {FD_STDIN, FD_STDOUT, FD_STDERR,FD_EVENTS,FD_DISPINFO, FD_FB};

size_t invalid_read(void *buf, size_t offset, size_t len) {
  panic("should not reach here");
  return 0;
}

size_t invalid_write(const void *buf, size_t offset, size_t len) {
  panic("should not reach here");
  return 0;
}

/* This is the information about all files in disk. */
static Finfo file_table[] __attribute__((used)) = {
  [FD_STDIN]  = {"stdin", 0, 0, invalid_read, invalid_write},
  [FD_STDOUT] = {"stdout", 0, 0, invalid_read, serial_write},
  [FD_STDERR] = {"stderr", 0, 0, invalid_read, serial_write},
  [FD_EVENTS] = {"/dev/events", 0, 0, events_read, invalid_write},
  [FD_DISPINFO] = {"/proc/dispinfo", 0, 0, dispinfo_read, invalid_write},
  [FD_FB] = {"/dev/fb", 0, 0, invalid_read, fb_write},
#include "files.h"
  
};
int fs_open(const char *pathname, int flags, int mode){
  int i = 0;
  // printf("pathname=%s\n",pathname);
  while (file_table[i].name != NULL)
  {
    if (!strcmp(file_table[i].name,pathname))
    {
      file_table[i].open_offset = 0;
      return i;
    }
    i ++;
  }
  assert(0);
  return -1;
}
size_t  fs_read(int fd, void *buf, size_t len){
  if (fd == 0)
  {
    panic("error !!!! this file cannt read");
    return -1;
  }
  if (file_table[fd].read)
  {
    // int re_read = file_table[fd].read(buf,file_table[fd].open_offset ,len);
    // file_table[fd].open_offset = file_table[fd].open_offset+len;
    return file_table[fd].read(buf,file_table[fd].open_offset ,len);
  }
  size_t left=file_table[fd].size-file_table[fd].open_offset;
  if(left<len) len=left;
  ramdisk_read(buf,file_table[fd].disk_offset+file_table[fd].open_offset,len);
  file_table[fd].open_offset += len;
  return  len;
}
size_t fs_write(int fd, const void *buf, size_t len){
  if (file_table[fd].write)
  {
    // int re_write = file_table[fd].write(buf,file_table[fd].open_offset ,len);
    // file_table[fd].open_offset = file_table[fd].open_offset+len;   
    return file_table[fd].write(buf,file_table[fd].open_offset ,len);
  }
  
  if (len > file_table[fd].size )
  {
    printf("fd = %d  len = %ld,size = %ld",fd,len,file_table[fd].size);
    panic("write file offset out size");
    return -1;
  }
  ramdisk_write(buf,file_table[fd].disk_offset+file_table[fd].open_offset,len);
  file_table[fd].open_offset = file_table[fd].open_offset+len;
  return len;
}
size_t fs_lseek(int fd, size_t offset, int whence){
  switch (whence)
  {
  case SEEK_SET:file_table[fd].open_offset= offset;break;
  case SEEK_CUR: file_table[fd].open_offset += offset;break;
  case SEEK_END:file_table[fd].open_offset = offset + file_table[fd].size;break;
  default:return -1;
    break;
  }
  return file_table[fd].open_offset;
}
int fs_close(int fd){
  return 0;
}
void init_fs() {
}
